/*
 * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * Copyright (C) 2021- SmartUt contributors
 *
 * SmartUt is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * SmartUt is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with SmartUt. If not, see <http://www.gnu.org/licenses/>.
 */
package org.smartut.jenkins.actions;

import org.smartut.Properties;
import org.smartut.jenkins.plot.CoveragePlot;
import org.smartut.jenkins.plot.TimePlot;
import org.smartut.jenkins.recorder.SmartUtRecorder;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import hudson.EnvVars;
import hudson.FilePath;
import hudson.maven.AbstractMavenProject;
import hudson.maven.MavenModule;
import hudson.maven.MavenModuleSet;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.remoting.VirtualChannel;

public class ProjectAction implements Action {

	private final AbstractProject<?, ?> project;

	private final List<ModuleAction> modules;

	public ProjectAction(AbstractProject<?, ?> project) {
		this.project = (AbstractProject<?, ?>) project;
		this.modules = new ArrayList<ModuleAction>();
	}

	public ProjectAction(AbstractProject<?, ?> project, List<ModuleAction> modules) {
		this.project = (AbstractProject<?, ?>) project;
		this.modules = new ArrayList<ModuleAction>(modules);
	}

	@Override
	public String getIconFileName() {
		return "/plugin/smartut-jenkins-plugin/icons/smartut.png";
	}

	@Override
	public String getDisplayName() {
		return "SmartUt Project Statistics";
	}

	@Override
	public String getUrlName() {
		return "smartut-project";
	}

	public AbstractProject<?, ?> getProject() {
		return this.project;
	}

	public String getName() {
		return this.project.getName();
	}

	public List<ModuleAction> getModules() {
		return this.modules;
	}

	public void perform(AbstractMavenProject<?, ?> project, AbstractBuild<?, ?> build,
			BuildListener listener) throws InterruptedException, IOException {

		EnvVars env = build.getEnvironment(listener);
		env.overrideAll(build.getBuildVariables());

		VirtualChannel channel = build.getWorkspace().getChannel();

		MavenModuleSet prj = (MavenModuleSet) this.project;
		for (MavenModule module : prj.getModules()) {

		  FilePath fp = new FilePath(channel, build.getWorkspace().getRemote() + File.separator
              + (module.getRelativePath().isEmpty() ? "" : module.getRelativePath() + File.separator)
              + Properties.CTG_DIR + File.separator + Properties.CTG_PROJECT_INFO);

		  if (!fp.exists()) {
		    listener.getLogger().println(SmartUtRecorder.LOG_PREFIX + "There is not any " +
		        fp.getRemote() + " file for module " + module.getName());
		    continue ;
		  }

		  ByteArrayOutputStream out = new ByteArrayOutputStream();
		  fp.copyTo(out);
		  ByteArrayInputStream projectXML = new ByteArrayInputStream(out.toByteArray());

		  listener.getLogger().println(SmartUtRecorder.LOG_PREFIX + "Analysing " +
		      Properties.CTG_PROJECT_INFO + " file from " + fp.getRemote());

		  ModuleAction m = new ModuleAction(build, module.getName());
		  if (!m.build(channel, projectXML, listener)) {
		    continue ;
		  }

		  this.modules.add(m);
		}
	}

	public void doCoverageGraph(StaplerRequest req, StaplerResponse rsp) throws IOException {
		CoveragePlot c = new CoveragePlot(this, "Coverage %");
		c.doCoverageGraph(req, rsp);
	}

	public void doCoverageMap(StaplerRequest req, StaplerResponse rsp) throws IOException {
		CoveragePlot c = new CoveragePlot(this, "Coverage");
		c.doCoverageMap(req, rsp);
	}

	public void doTimeGraph(StaplerRequest req, StaplerResponse rsp) throws IOException {
		TimePlot c = new TimePlot(this, "Time (minutes)");
		c.doTimeGraph(req, rsp);
	}

	public void doTimeMap(StaplerRequest req, StaplerResponse rsp) throws IOException {
		TimePlot c = new TimePlot(this, "Time");
		c.doTimeMap(req, rsp);
	}
	
	// data for jelly template

	public int getNumberOfModules() {
		return this.modules.size();
	}

	public int getNumberOfTestableClasses() {
		if (this.modules.isEmpty()) {
			return 0;
		}

		int classes = 0;
		for (ModuleAction m : this.modules) {
			classes += m.getNumberOfTestableClasses();
		}

		return classes;
	}

	public int getNumberOfTestedClasses() {
		if (this.modules.isEmpty()) {
			return 0;
		}

		int classes = 0;
		for (ModuleAction m : this.modules) {
			classes += m.getNumberOfTestedClasses();
		}

		return classes;
	}

	public Set<String> getCriteria() {
		Set<String> criteria = new LinkedHashSet<String>();
		if (this.modules.isEmpty()) {
			return criteria;
		}

		for (ModuleAction m : this.modules) {
			criteria.addAll(m.getCriteria());
		}

		return criteria;
	}

	public double getOverallCoverage() {
		if (this.modules.isEmpty()) {
			return 0.0;
		}

		double coverage = 0.0;
		for (ModuleAction m : this.modules) {
			coverage += m.getOverallCoverage();
		}

		DecimalFormat formatter = SmartUtRecorder.decimalFormat;
		formatter.applyPattern("#0.00");
		return Double.parseDouble(formatter.format(coverage / this.modules.size()));
	}

	public double getCriterionCoverage(String criterionName) {
		if (this.modules.isEmpty()) {
			return 0.0;
		}

		double coverage = 0.0;
		for (ModuleAction m : this.modules) {
			coverage += m.getAverageCriterionCoverage(criterionName);
		}

		DecimalFormat formatter = SmartUtRecorder.decimalFormat;
		formatter.applyPattern("#0.00");
		return Double.parseDouble(formatter.format(coverage / this.modules.size()));
	}

	public int getTotalEffort() {
		if (this.modules.isEmpty()) {
			return 0;
		}

		int effort = 0;
		for (ModuleAction m : this.modules) {
			effort += m.getTotalEffort();
		}

		return effort;
	}

	public int getTimeBudget() {
      if (this.modules.isEmpty()) {
          return 0;
      }

      int effort = 0;
      for (ModuleAction m : this.modules) {
          effort += m.getTimeBudget();
      }

      return effort;
  }
}
